// networkHandler.cpp
// Written by Sam Tarin, Andi Cescolini, and Kevin Sonnen

#include "networkHandler.h"
#include <string>
#include <boost/array.hpp>
#include <boost/asio.hpp>
#include <iostream>
#include <vector>

// Code snipets taken from example code:
// chat_client.cpp

//#define DEBUG

using boost::asio::ip::tcp;

typedef std::deque<sit_wrap::situation_wrapper> game_situation_queue;

namespace networkI {
	//new constructor
	networkHandler::networkHandler(){
		needToReconcile = false;
		Mysocket = new boost::asio::ip::tcp::socket(Myio_service); 

		tcp::resolver resolver(Myio_service);
		tcp::resolver::query query("127.0.0.1", "13000");
		Myiterator = resolver.resolve(query);
				
		boost::asio::async_connect(*Mysocket, Myiterator, boost::bind(&networkHandler::handle_connect, this, boost::asio::placeholders::error));
		//SituationController situationControl;
		//SitControl = &situationControl;
	}

	// Overloaded constructor, takes a reference to a situation controller object
	/*networkHandler::networkHandler(SituationController* situationControl){
		Mysocket = new boost::asio::ip::tcp::socket(Myio_service); 

		tcp::resolver resolver(Myio_service);
		tcp::resolver::query query("127.0.0.1", "13000");
		Myiterator = resolver.resolve(query);

		boost::asio::async_connect(*Mysocket, Myiterator, boost::bind(&networkHandler::handle_connect, this, boost::asio::placeholders::error));

		SitControl = situationControl;
	}*/

	networkHandler::~networkHandler(){
		//call disconnect?
	}

	int networkHandler::disconnect(){
		close();
		//Mythread->join();

		return 0;
	}


	void networkHandler::write(const sit_wrap::situation_wrapper& msg)
	{
		//std::cout << "Client just got into write\n";
		Myio_service.post(boost::bind(&networkHandler::do_write, this, msg));
		#ifdef DEBUG
		printf("client is in the write function\n");
		#endif
	}

	void networkHandler::close()
	{
		Myio_service.post(boost::bind(&networkHandler::do_close, this));
		#ifdef DEBUG
	  	printf("client is in the close function\n");
		#endif
	}


	int networkHandler::sendSituation(Situation sit){
		#ifdef DEBUG
		printf("client is in send situation\n");
		#endif
		sit_wrap::situation_wrapper wrapped_sit(sit);
		wrapped_sit.serialize();
		#ifdef DEBUG
		printf("client called serialize\n");
		#endif
		wrapped_sit.encode_header();
		#ifdef DEBUG
		printf("client is about to call write\n");
		#endif
		write(wrapped_sit);

		return 0;
	}

	Situation networkHandler::recvSituation(sit_wrap::situation_wrapper sw){
		#ifdef DEBUG
		printf("client is in receive situation\n");
		#endif
		return sw.deserialize();
	}

	void networkHandler::handle_connect(const boost::system::error_code& error)
	{
		fprintf(stderr, "got to handle_connect()");
		if (!error)
		{
			#ifdef DEBUG
			printf("in handle connect\n");
			#endif
			boost::asio::async_read(*Mysocket,
				boost::asio::buffer(read_msg_.data(), sit_wrap::situation_wrapper::header_length),
				boost::bind(&networkHandler::handle_read_header, this,
				boost::asio::placeholders::error));
			#ifdef DEBUG
			std::cout << "printing header length " << sit_wrap::situation_wrapper::header_length << std::endl;
			#endif
		}
		else
		{
			#ifdef DEBUG
			std::cout << "Error in handle_connect: " << error.message() << "\n";
			#endif
			do_close();
		}
  	}

	void networkHandler::handle_read_header(const boost::system::error_code& error)
	{
		#ifdef DEBUG
		printf("client is in handle read header\n");
		#endif
		if (!error && read_msg_.decode_header())
		{
			boost::asio::async_read(*Mysocket,
				boost::asio::buffer(read_msg_.body(), read_msg_.body_length()),
				boost::bind(&networkHandler::handle_read_body, this,
				boost::asio::placeholders::error));
		#ifdef DEBUG
		std::cout << "printing body length " << read_msg_.body_length() << std::endl;
		#endif
	 	}
		else
		{
			#ifdef DEBUG
			std::cout << "Error in handle_read_header: " << error.message() << "\n";
			#endif
			do_close();
		}
	}

	void networkHandler::handle_read_body(const boost::system::error_code& error)
	{
		if (!error)
		{
			#ifdef DEBUG
			printf("client is in handle read body\n");
			#endif
			MySituation = recvSituation(read_msg_);
			#ifdef DEBUG
			std::cout << "Received situation from server, player x pos: " << MySituation.getPlayer().getXPos() << std::endl;
			#endif
			// Call situationController::reconcile()
			//SitControl->reconcile(MySituation);
			needToReconcile = true;

			boost::asio::async_read(*Mysocket,
				boost::asio::buffer(read_msg_.data(), sit_wrap::situation_wrapper::header_length),
				boost::bind(&networkHandler::handle_read_header, this,
				boost::asio::placeholders::error));
		}
		else
		{
			#ifdef DEBUG
			std::cout << "Error in handle_read_body: " << error.message() << "\n";
			#endif
			do_close();
		}
	}

	void networkHandler::do_write(sit_wrap::situation_wrapper msg)
	{
		#ifdef DEBUG
		printf("client is in do write\n");
		#endif
		bool write_in_progress = !write_msgs_.empty();
		write_msgs_.push_back(msg);
		#ifdef DEBUG
		std::cout << "printing data in do write " << write_msgs_.front().data() << std::endl; 
		#endif

                #ifdef DEBUG
        	std::cout << "printing length in do write " << write_msgs_.front().length() << std::endl;
                #endif

		if (!write_in_progress)
		{
                	#ifdef DEBUG
		        printf("in write_in_progress\n");
                	#endif
			boost::asio::async_write(*Mysocket,
				boost::asio::buffer(write_msgs_.front().data(),
				write_msgs_.front().length()),
				boost::bind(&networkHandler::handle_write, this,
				boost::asio::placeholders::error));
		}
	}

	void networkHandler::handle_write(const boost::system::error_code& error)
	{
		#ifdef DEBUG
		printf("client is in handle write\n");
		#endif
		if (!error)
		{
			write_msgs_.pop_front();
			if (!write_msgs_.empty())
			{
				boost::asio::async_write(*Mysocket,
					boost::asio::buffer(write_msgs_.front().data(),
					write_msgs_.front().length()),
					boost::bind(&networkHandler::handle_write, this,
					boost::asio::placeholders::error));
			}
		}
		else
		{
			#ifdef DEBUG
			std::cout << "Error in handle_write: " << error.message() << "\n";
			#endif
			do_close();
		}
	}

	void networkHandler::do_close()
	{
		#ifdef DEBUG
		printf("client is in do_close\n");
		#endif
		Mysocket->close();
	}

}
